﻿using Core.Tools;
using System;

namespace Core.ObjectOperate.ReflectionImpl
{
    public sealed class IndexInfo
    {
        public static IndexInfo Create(System.Reflection.PropertyInfo IndexInfo, bool NonPublic)
        {
            if (IndexInfo == null)
            {
                throw new NullReferenceException("IndexInfo");
            }

            return new IndexInfo(IndexInfo, NonPublic);
        }
        
        private readonly MethodInfo GetMethod;
        private readonly MethodInfo SetMethod;

        private readonly Type InternalType;
        private readonly Type[] InternalParamsTypes;

        private bool InternalCanRead;
        private bool InternalCanWrite;

        public Type Type { get { return InternalType; } }

        public bool CanRead { get { return InternalCanRead; } }

        public bool CanWrite { get { return InternalCanWrite; } }

        public Type[] ParamsTypes { get { return (Type[])InternalParamsTypes.Clone(); } }

        private IndexInfo(System.Reflection.PropertyInfo Content, bool NonPublic)
        {
            InternalType = Content.PropertyType;
            InternalParamsTypes = TypeHelper.GetParamsTypes(Content.GetIndexParameters());

            System.Reflection.MethodInfo TempMethod;

            if (Content.CanRead && (TempMethod = Content.GetGetMethod(true)) != null)
            {
                GetMethod = MethodInfo.Create(TempMethod);
            }

            if (Content.CanWrite && (TempMethod = Content.GetSetMethod(true)) != null)
            {
                SetMethod = MethodInfo.Create(TempMethod);
            }

            SetNonPublic(NonPublic);
        }

        internal void SetNonPublic(bool NonPublic)
        {
            InternalCanRead = GetMethod != null && (NonPublic || GetMethod.IsPublic);
            InternalCanWrite = SetMethod != null && (NonPublic || SetMethod.IsPublic);
        }

        public void SetValue(object Object, object[] Params, object Value)
        {
            if (!InternalCanWrite)
            {
                throw new ArgumentException("This indexer no set method.");
            }

            SetMethod.Invoke(Object, Params, Value);
        }

        public object GetValue(object Object, object[] Params)
        {
            if (!InternalCanRead)
            {
                throw new ArgumentException("This indexer no get method.");
            }

            return GetMethod.Invoke(Object, Params);
        }
    }
}